﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Diagnostics;
using YetAnotherChat.TcpServer.WebSocketProtocols;
using System.Threading;

namespace YetAnotherChat.TcpServer
{
    /// <summary>
    /// Inherits from TcpServer, start exe when got a connection (client) from TcpServer.
    /// Wrap TcpClient in a Connection object, manage list of connection objects
    /// Raise events when someone connecting, connected, disconnected, msg come, msg sent
    /// </summary>
    public class WebSocketServer : TcpServer, IDisposable
    {
       

        private List<Connection> CurrentConnections = new List<Connection>();

        public event EventHandler<ConnectionEventArgs> ClientConnecting;
        public event EventHandler<ConnectionEventArgs> ClientConnected;
        public event EventHandler<ConnectionEventArgs> ClientDisconnecting;
        public event EventHandler<MessageEventArgs> MessageReceived;
        public event EventHandler<MessageEventArgs> MessgeSent;

        protected internal void OnClientConnecting(ConnectionEventArgs e)
        {
            if(ClientConnecting !=null)
                ClientConnecting(this, e);
        }

        protected internal void OnClientConnected(ConnectionEventArgs e)
        {
            if (ClientConnected != null)
                ClientConnected(this, e);
        }

        protected internal void OnClientDisconnecting(ConnectionEventArgs e)
        {
            if (ClientDisconnecting != null)
                ClientDisconnecting(this, e);
        }

        protected internal void OnMessageReceived(MessageEventArgs e)
        {
            if (MessageReceived != null)
                MessageReceived(this, e);
        }

        protected internal void OnMessgeSent(MessageEventArgs e)
        {
            if (MessgeSent != null)
                MessgeSent(this, e);
        }
        
        
        /// <summary>
        /// Configuration for the above heartbeat setup.
        /// TimeOut : How long until a connection drops when it doesn't receive anything.
        /// MaxPingsInSequence : A multiple of TimeOut for how long a connection can remain idle(only pings received) before we kill it.
        /// </summary>
        public TimeSpan TimeOut = TimeSpan.FromMinutes(1);

        /// <summary>
        /// A list of acceptable subprotocols that this server supports.
        /// See http://tools.ietf.org/html/rfc6455#section-1.9
        /// </summary>
        public string[] SubProtocols;

        private string _destination = String.Empty;
        private string _origin = String.Empty;

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
        /// </summary>
        public WebSocketServer(int listenPort = 0, IPAddress listenAddress = null) : base(listenPort, listenAddress) { }

        /// <summary>
        /// Gets or sets the origin host.
        /// </summary>
        /// <value>
        /// The origin host.
        /// </value>
        public string Origin
        {
            get { return _origin; }
            set
            {
                _origin = value;
                AuthenticationBase.Origin = value;
            }
        }

        /// <summary>
        /// Gets or sets the destination host.
        /// </summary>
        /// <value>
        /// The destination host.
        /// </value>
        public string Destination
        {
            get { return _destination; }
            set
            {
                _destination = value;
                AuthenticationBase.Destination = value;
            }
        }


        /// <summary>
        /// Fires when a client connects.
        /// </summary>
        /// <param name="data">The TCP Connection.</param>
        public override void OnConnectionArrive(object data)
        {
            var connection = new ServerConnection(this, (TcpClient)data, new WebSocketProtocolFactory());
            Debug.WriteLine(string.Format("Created context to execute connection, client address {0}", connection.TcpClient.Client.RemoteEndPoint));
            
            connection.BufferSize = BufferSize;
            OnClientConnecting(new ConnectionEventArgs(connection));
            Debug.WriteLine(string.Format("Raised client connecting event"));


            ThreadPool.QueueUserWorkItem(SetupConnection, connection);
            
        }

        void SetupConnection(object data)
        {
            var connection = (Connection)data;
            connection.SetupSendReceiveProcess();
            if (connection.Connected)
            {
                lock (CurrentConnections)
                {
                    CurrentConnections.Add(connection);
                }
            }
        }

        internal void RemoveConnection(Connection con)
        {
            lock (CurrentConnections)
            {
                CurrentConnections.Remove(con);
                Debug.WriteLine(string.Format("removed a connection from server, address {0}", con.ClientAddress));
            }
        }

        public void Dispose()
        {
            //cancellation.Cancel();
            base.Dispose();
            //Handler.Instance.Dispose();
        }


        public void Broadcast(Guid fromConnection, string message)
        {
            if (CurrentConnections != null)
            {
                foreach (var c in CurrentConnections)
                {
                    if (c.ConnectionId != fromConnection)
                        c.Send(message);
                }
            }
        }
    }
}
